package org.example.java.jvm.error;

import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc
 * <p/>
 * 这就是问题的根源——对象创建的速度要比Finalizer线程调用finalize()结束它们的速度要快
 * <p/>
 * <p/>
 * JVM创建Finalizable对象
 * JVM创建 java.lang.ref.Finalizer实例，指向刚创建的对象。
 * java.lang.ref.Finalizer类持有新创建的java.lang.ref.Finalizer的实例。这使得下一次新生代GC无法回收这些对象。
 * 新生代GC无法清空Eden区，因此会将这些对象移到Survivor区或者老生代。
 * 垃圾回收器发现这些对象实现了finalize()方法。因为会把它们添加到java.lang.ref.Finalizer.ReferenceQueue队列中。
 * Finalizer线程会处理这个队列，将里面的对象逐个弹出，并调用它们的finalize()方法。
 * finalize()方法调用完后，Finalizer线程会将引用从Finalizer类中去掉，因此在下一轮GC中，这些对象就可以被回收了。
 * Finalizer线程会和我们的主线程进行竞争，不过由于它的优先级较低，获取到的CPU时间较少，因此它永远也赶不上主线程的步伐。
 * 程序消耗了所有的可用资源，最后抛出OutOfMemoryError异常
 *
 * @author juntao zhang
 */
class FinalizeTest {
    static AtomicInteger aliveCount = new AtomicInteger(0);

    FinalizeTest() {
        aliveCount.incrementAndGet();
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        FinalizeTest.aliveCount.decrementAndGet();
    }

    public static void main(String args[]) {
        for (int i = 0; ; i++) {
            //            Object o = new Object();//this will not lead to OOM
            FinalizeTest f = new FinalizeTest();
            if ((i % 100_000) == 0) {
                System.out.format("After creating %d objects, %d are still alive.%n", i,
                    FinalizeTest.aliveCount.get());
            }
        }
    }
}
/*
98% of the total time doing GC and when after the GC only less than 2% of the heap is recovered.

java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.ref.Finalizer.register(Finalizer.java:87)
	at java.lang.Object.<init>(Object.java:37)
	at org.example.java.jvm.error.FinalizeTest.<init>(FinalizeTest.java:26)
	at org.example.java.jvm.error.FinalizeTest.main(FinalizeTest.java:39)
*/